package com.mf.all.exception;

import com.mf.all.result.Result;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.sql.SQLException;
import java.util.List;

/**
 * @Author: lmf
 * @Create: 2025/3/8 20:53
 * Description:  控制层异常处理
 */
@RestControllerAdvice
public class GraceExceptionHandler {
    @ExceptionHandler(value = ApiException.class)
    public Result<String> apiException(ApiException e) {
        return Result.fail(e);
    }

    /**
     * Http 方法检测
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public Result<String> returnHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return Result.fail("请检查调用【方法类型】是否正常：" + e.getMessage());
    }

    /**
     * 数据库语句错误
     */
    @ExceptionHandler(BadSqlGrammarException.class)
    public Result<String> sqlException(Exception e) {
        String message = e.getMessage();
        String[] split = message.split("###");
        split = split[2].replace("]", "").split("\\\\");
        String xml = split[split.length - 1].replace("\n", "");
        String sql = message.split("### Cause: ")[0].split("### SQL: ")[1].replace("\n", "");
        while (sql.contains("  ")) {
            sql = sql.replace("  ", " ");
        }
        System.out.println("==========================");
        System.out.println("SQL语句发生错误");
        System.out.println("xml: " + xml);
        System.out.println("sql：" + sql);
        System.out.println("==========================");
        return Result.fail("SQL语句发生错误:" + e.getMessage());
    }

    /**
     *
     */
    @ExceptionHandler(NoResourceFoundException.class)
    public Result<String> returnNoResourceFoundException(Exception e) {
        return Result.fail("找不到资源：" + e.getMessage());
    }


    /**
     * Http消息不可读异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public Result<String> returnHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        return Result.fail(e.getMessage());
    }

    /**
     * get请求必填参数验证异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public Result<String> returnMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        return Result.fail(e.getMessage());
    }

    /**
     * 处理方法参数不合法的异常。
     * 当客户端提交的请求参数不满足验证条件时，会抛出此异常。
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result<String> returnMethodArgumentNotValid(MethodArgumentNotValidException e) {
        // 获取验证结果
        BindingResult result = e.getBindingResult();
        // 根据验证结果生成错误信息，并返回失败的结果对象
        return Result.fail(getErrors(result));
    }

    /**
     * 空指针
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public Result<String> returnNullPointerException(NullPointerException e) {
        return Result.fail("空指针异常:" + e.getMessage());
    }

    /**
     * 处理mysql错误异常
     */
    @ExceptionHandler(SQLException.class)
    @ResponseBody
    public Result<String> returnDataIntegrityViolationException(SQLException e) {
        if (e.getMessage().contains("Duplicate entry")) {
            return Result.fail("数据重复: " + e.getMessage());
        }
        return Result.fail(e.getMessage());
    }


    /**
     * 获取数据绑定错误信息。
     * 从{@link BindingResult}中提取字段错误信息，并将其映射为字段名到错误信息的映射表。
     */
    public String getErrors(BindingResult result) {
        StringBuilder message = new StringBuilder();
        List<FieldError> errorList = result.getFieldErrors();
        for (FieldError ff : errorList) {
            // 将每个字段错误的信息添加到映射表中
            String field = ff.getField();
            String msg = ff.getDefaultMessage();
            message.append(field).append(":").append(msg).append(";");
        }
        return message.toString();
    }
}
